import React from 'react';
import styles from '@/pages/watch/board/index.less';
import User from '@/pages/watch/board/user';
import SocketEvent from '@/service/event';
import {btnSound, moveSound, peaceSound, playSound, startSound} from '@/utils/sounds-res';
import {Modal, Toast} from 'antd-mobile';
import {log} from '@/utils/log-utils';
import CheckWin from '@/utils/check-win';
import * as boardCanvas from "@/utils/board-canvas-utils";
import AdvancedSpin from "@/spinner";

/**
 * 观战棋盘
 */
class WatchBoard extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      loading: false,
      text: '',
      /** -- 棋盘初始化时的一些状态 -- */
      gameMap: null,
      // 棋子被选中时外套一个盒子，此属性决定该盒子是否显示
      chessBox: {show: false, color: null, x: 0, y: 0},
      /** -- 游戏进行时的一些状态 -- */
      // 落子方（象棋规则是红先黑后）
      isRedMove: true,
      // 游戏是否结束
      gameOver: false,
      // 游戏结束后的提示信息
      gameOverMsg: null,
      /** -- 用户信息 -- */
      user: null,
      enemy: null,
    };

    // 事件回调id
    this.moveChessRespId = 'watchMoveChessRespId';
    this.syncBattleDataRespId = 'watchSyncBattleDataRespId';
    this.gameWinRespId = 'watchGameWinRespId';
    this.backMoveRespId = 'watchBackMoveRespId';
    this.backMoveResultRespId = 'watchBackMoveResultRespId';
    this.userTimeRespId = 'watchUserTimeRespId';
    this.enemyJoinRoomRespId = 'watchEnemyJoinRoomRespId';
    this.enemyLeaveRoomRespId = 'watchEnemyLeaveRoomRespId';
    this.allowInBattleId = 'watchAllowInBattleId';
    this.sessionRecoverRespId = 'watchSessionRecoverRespId';
    this.sessionRecoverNotifyId = 'watchSessionRecoverNotifyId';

    this.unRegisterServices = [];
    this.unRegisterServices.push(this.moveChessRespId);
    this.unRegisterServices.push(this.syncBattleDataRespId);
    this.unRegisterServices.push(this.gameWinRespId);
    this.unRegisterServices.push(this.backMoveRespId);
    this.unRegisterServices.push(this.backMoveResultRespId);
    this.unRegisterServices.push(this.userTimeRespId);
    this.unRegisterServices.push(this.enemyJoinRoomRespId);
    this.unRegisterServices.push(this.enemyLeaveRoomRespId);
    this.unRegisterServices.push(this.allowInBattleId);
    this.unRegisterServices.push(this.sessionRecoverRespId);
    this.unRegisterServices.push(this.sessionRecoverNotifyId);

    // 求和、悔棋枚举
    this.agree = 'agree';
    this.reject = 'reject';
  }

  componentDidMount() {
    // 绑定节点
    boardCanvas.initBinds(this.boardRef);
    // 初始化棋盘数据
    this.initChessBoard();
    // 事件监听
    this.startEventListen();
    // 开启倒计时
    this.startCountTime();
    // 加入观战播放声音
    playSound(startSound);
  }

  /**
   * 初始化棋盘的数据
   */
  initChessBoard = () => {
    const {user} = this.props;
    const gameMap = user.gameMap;
    const chessBox = {
      ...user.chessBox,
      show: user.stepCount !== 0,
    };
    this.setState({
      ...this.props,
      gameMap,
      chessBox,
      isRedMove: user.isRedMove,
    });
    boardCanvas.initMap(gameMap);
    boardCanvas.setChessBox(chessBox);
  }

  /**
   * 开启游戏计时
   */
  startCountTime = () => {
    log(`已开启观战游戏计时`);
    // 进行倒计时
    this.intervalId = setInterval(() => {
      const {user, enemy, isRedMove} = this.state;
      if (user && enemy) {
        // 计时方
        const roomUser = user.first === isRedMove ? user : enemy;
        // 客户端计时只是展示效果，实际计时由服务器完成
        // 所以当客户端计时小于0时不再进行任何操作，服务器计时结束后会自动判定输赢
        if (roomUser.stepTime >= 0 || roomUser.allTime >= 0) {
          // 步时减一
          roomUser.stepTime > 0 && (roomUser.stepTime = roomUser.stepTime - 1);
          // 进入读秒
          roomUser.allTime === 1 && (roomUser.stepTime = roomUser.readSeconds);
          // 局时
          roomUser.allTime > 0 && (roomUser.allTime = roomUser.allTime - 1);
          // 根据落子方更新数据
          if (user.first === isRedMove) {
            this.setState({user: roomUser});
          } else {
            this.setState({enemy: roomUser});
          }
        }
      }
    }, 1000);
  };

  /**
   * 事件监听
   */
  startEventListen = () => {

    // 会话恢复结果通知
    SocketEvent.register('sessionRecoverNotifyApi', this.sessionRecoverNotifyId, resp => {
      log(`(观战)会话恢复结果通知返回：`, resp);
      if (resp.code === 'success') {
        // 如果服务器有battleId，则以服务器的为准
        // 因为用户切后台后，新开的对局事件收不到，导致对战数据同步失败
        const {battleId, isNewBattle} = resp.data;
        isNewBattle && Toast.show('双方已重新开局');

        // 主动同步服务器的数据
        SocketEvent.emit(this.props.socket, 'syncBattleDataApi', {
          userId: this.props.userId,
          roomId: this.props.roomId,
          battleId: battleId || this.props.battleId,
        }, resp => {
          this.setState({loading: false});
          if (resp.code === 'success') {
            const {gameOver} = this.state;
            // 如果游戏是结束状态，初始化计时等数据
            if (gameOver) {
              this.setState({
                gameOver: false,
              });
              // 开始倒计时
              this.startCountTime();
            }
          } else {
            Toast.show(resp.msg);
          }
        });
      }
    });

    /**
     * 会话恢复通知
     * 仅处理00004消息类
     */
    SocketEvent.register('sessionRecoverRespApi', this.sessionRecoverRespId, resp => {
      log(`(观战)会话恢复结果通知返回：`, resp);
      if (resp.code === 'S000004') {
        // 卸载所有已注册的事件
        clearInterval(this.intervalId);
        this.setState({gameOver: true, gameOverMsg: '已结束'});

        Modal.alert('系统提示', resp.msg, [
          {
            text: '知道了', onPress: () => {
              playSound(btnSound);
            },
          }
        ]);
      }
    });

    // 对手加入我方房间监听
    SocketEvent.register('enemyJoinRoomRespApi', this.enemyJoinRoomRespId, resp => {
      log('对方加入我方房间，resp：', resp);
      if (resp.code === 'success') {
        const {user} = resp.data || {};
        if (user && user.first) {
          // 若交换了先手方，则清除对方的信息
          if (this.state.enemy?.userId === user.userId) {
            this.setState({enemy: this.state.user});
          }
          this.setState({user: user});
        } else if (user && !user.first) {
          // 若交换了先手方，则清除对方的信息
          if (this.state.user?.userId === user.userId) {
            this.setState({user: this.state.enemy});
          }
          this.setState({enemy: user});
        }
      }
    });

    // 对手离开房间监听
    SocketEvent.register('enemyLeaveRoomRespApi', this.enemyLeaveRoomRespId, resp => {
      log('(观战)选手离开房间：', resp);
      const {user, enemy} = this.state;
      const leaveUserId = resp.data.userId;
      let leaveUserName;
      if (user && user.userId === leaveUserId) {
        leaveUserName = user.userName;
        this.setState({user: null});
      } else if (enemy && enemy.userId === leaveUserId) {
        leaveUserName = enemy.userName;
        this.setState({enemy: null});
      }
      // 重新获取数据并判断
      if (this.state.user || this.state.enemy) {
        Toast.show(`[${leaveUserName || leaveUserId}]离开了房间`);
      } else if (!this.state.user && !this.state.enemy) {
        Toast.show(`双方已离开房间`);
      }
    });

    // 双方已准备，监听到此项表示双方在此房间又重新开始的对局
    SocketEvent.register('allowInBattleApi', this.allowInBattleId, (resp) => {
      log('(观战)服务器允许双方进入对局，resp: ', resp);
      if (resp.code === 'success') {
        // 让用户加入该房间
        SocketEvent.emit(this.props.socket, 'joinWatchApi', {
          userId: this.props.userId,
          roomId: this.props.roomId,
        });
        // 处理对局数据
        const {playOne, playTwo} = resp.data;
        const roomUser = playOne.first ? playOne : playTwo;
        const roomEnemy = playOne.first ? playTwo : playOne;
        const gameMap = roomUser.gameMap;
        const chessBox = {show: false};
        log(`(观战)即将重新开局, 开局数据：`, {roomUser, roomEnemy});

        this.setState({
          user: roomUser,
          enemy: roomEnemy,
          gameOver: false,
          gameMap: gameMap,
          chessBox: chessBox,
        });
        boardCanvas.initMap(gameMap);
        boardCanvas.setChessBox(chessBox);
        // 开始倒计时
        this.startCountTime();
        // 播放开局声音
        playSound(startSound);
        // 提示
        Toast.show('双方已准备，对局开始');
      } else {
        Toast.show(resp.msg);
      }
    });

    // 对局倒计时监听
    SocketEvent.register('userTimeRespApi', this.userTimeRespId, resp => {
      log('观战倒计时返回：', resp);
      if (resp.code === 'success') {
        const {user, enemy} = this.state;
        const {userList} = resp.data || [];
        const userData = userList.find(item => item.userId === user.userId);
        if (userData) {
          user.stepTime = userData.stepTime;
          user.allTime = userData.allTime;
        }
        const enemyData = userList.find(item => item.userId === enemy.userId);
        if (enemyData) {
          enemy.stepTime = enemyData.stepTime;
          enemy.allTime = enemyData.allTime;
        }
        this.setState({user, enemy});
      }
    });

    // 同步对战的数据
    SocketEvent.register('syncBattleDataRespApi', this.syncBattleDataRespId, resp => {
      log('观战用户同步房间数据返回：', resp);
      if (resp.code === 'success') {
        // 返回的对象
        const {roomUser, roomEnemy} = resp.data;
        // 落子方颜色
        const isRedMove = roomUser.isRedMove;
        // 落子方地图
        const gameMap = roomUser.gameMap;

        this.setState({
          user: roomUser,
          enemy: roomEnemy,
          gameMap: gameMap,
          isRedMove: isRedMove,
        });
        boardCanvas.initMap(gameMap);
        // 最后落子位置
        this.setState({chessBox: roomUser.chessBox});
        boardCanvas.setChessBox(roomUser.chessBox);
      }
    });

    // 注册棋子移动的回调事件
    SocketEvent.register('moveChessRespApi', this.moveChessRespId, resp => {
      log(`观战用户[${this.props.userId}]棋子移动, resp: `, resp);
      if (resp.code === 'success') {
        let {to} = resp.data;
        // 处理棋子移动
        this.handleChessMove(to, {show: true, x: to.x, y: to.y});
      } else {
        Toast.show(resp.msg);
      }
    });

    // 游戏结束后的弹窗
    SocketEvent.register('gameWinRespApi', this.gameWinRespId, (resp) => {
      log('观战游戏结束，resp: ', resp);
      if (resp.code === 'success') {
        const type = resp.data.type;
        const isRedColorWin = resp.data.isRedColorWin;

        playSound(peaceSound);
        let resultMsg = '';

        if (type === '0006') {
          resultMsg = '双方无可进攻棋子';
        } else if (type === '0005') {
          resultMsg = '双方议和';
        } else if (type === '0002') {
          resultMsg = (`${isRedColorWin ? '白棋' : '黑棋'}认输`);
        } else if (type === '0003') {
          resultMsg = (`${isRedColorWin ? '白棋' : '黑棋'}逃跑判负`);
        } else if (type === '0004') {
          resultMsg = (`${isRedColorWin ? '白棋' : '黑棋'}超时判负`);
        } else {
          resultMsg = (isRedColorWin ? '黑棋胜利' : '白棋胜利');
        }

        // 卸载所有已注册的事件
        clearInterval(this.intervalId);
        this.setState({gameOver: true, gameOverMsg: resultMsg});
      }
    });

    // 对方发起悔棋请求监听
    SocketEvent.register('backMoveRespApi', this.backMoveRespId, resp => {
      log('[观战]对方发起悔棋，返回：', resp);
      if (resp.code === 'success') {
        const {user} = this.state;
        const {userId} = resp.data;
        Toast.show(`${user.userId === userId ? `${user.first ? '黑棋' : '白棋'}` : `${user.first ? '白棋' : '黑棋'}`}请求悔棋`);
      }
    });

    // 对手方对『请求悔棋』意愿进行了处理
    SocketEvent.register('backMoveResultRespApi', this.backMoveResultRespId, resp => {
      log('[观战]对手方处理了悔棋意愿，返回：', resp);
      if (resp.code === 'success') {
        const {userId, result} = resp.data;
        const {user} = this.state;

        if (this.reject === result) {
          Toast.show(`${user.userId === userId ? `${user.first ? '黑棋' : '白棋'}` : `${user.first ? '白棋' : '黑棋'}`}拒绝悔棋`);
        } else {
          Toast.show(`${user.userId === userId ? `${user.first ? '黑棋' : '白棋'}` : `${user.first ? '白棋' : '黑棋'}`}同意悔棋`);
          // 主动同步服务器的数据
          SocketEvent.emit(this.props.socket, 'syncBattleDataApi', {
            userId: this.props.userId,
            roomId: this.props.roomId,
            battleId: this.props.battleId,
          }, resp => {
            if (resp.code !== 'success') {
              Toast.show(resp.msg);
              this.setState({loading: false});
            }
          });
        }
      } else if (resp.code === 'fail') {
        Toast.show(resp.msg);
      }
    });
  };

  /**
   * 处理棋子是移动或是吃棋
   * @param clickChess
   * @param chessBox
   */
  handleChessMove = (clickChess, chessBox) => {
    const {gameMap, user, enemy, stepCount, isRedMove} = this.state;
    playSound(moveSound);
    // 棋子信息 & 棋子盒子
    const chess = {...clickChess, isBlackColor: isRedMove}
    gameMap.push(chess)

    // 恢复双方倒计时
    user.stepTime = user.basicStepTime;
    enemy.stepTime = enemy.basicStepTime;

    // 走棋动画
    boardCanvas.moveChess(chess, gameMap);
    boardCanvas.setChessBox(chessBox);

    // 更新数据
    this.setState({
      stepCount: stepCount + 1,
      isRedMove: !isRedMove,
      gameMap,
    });

    // 检查游戏是否结束
    if (CheckWin(gameMap, chess)) {
      this.setState({gameOver: true});
    }
    return true;
  };

  /**
   * 重新设置地图的皮肤
   */
  resetSkinMap = () => {
    const {gameMap, chessBox} = this.state;
    boardCanvas.initMap(gameMap);
    boardCanvas.setChessBox(chessBox);
  }

  componentWillUnmount() {
    clearInterval(this.intervalId);
    SocketEvent.unRegister(this.unRegisterServices);
  }

  render() {
    const {user, enemy} = this.state;
    return (
      <div className={styles.bg}>
        <div className={styles.board} ref={ref => this.boardRef = ref}/>
        {/*对局人物组件*/}
        <User
          playOne={user || {}}
          playTwo={enemy || {}}
          userId={this.props.userId}
          userDetail={this.props.userDetail}
          isRedMove={this.state.isRedMove}
          gameOver={this.state.gameOver}
          gameOverMsg={this.state.gameOverMsg}
          socket={this.props.socket}
          roomId={this.props.roomId}
          resetSkinMap={this.resetSkinMap}
          goBack={() => this.props.goBack()}
        />
        <AdvancedSpin text={this.state.text} show={this.state.loading}/>
      </div>
    );
  }
}

export default WatchBoard;
